home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Networking / TPIFile1.0b1 / TPIFile.c < prev    next >
Encoding:
Text File  |  1997-04-10  |  59.3 KB  |  1,833 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        TPIFile.c
  3.  
  4.     Contains:    TPI Module to access File Manager files.  Technology
  5.                 demonstration only!
  6.  
  7.     Written by:    Quinn "The Eskimo!"
  8.  
  9.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Change History (most recent first):
  12.  
  13.     You may incorporate this sample code into your applications without
  14.     restriction, though the sample code has been provided "AS IS" and the
  15.     responsibility for its operation is 100% yours.  However, what you are
  16.     not permitted to do is to redistribute the source as "DSC Sample Code"
  17.     after having made changes. If you're going to re-distribute the source,
  18.     we require that you make it clear in the source that the code was
  19.     descended from Apple Sample Code, but that you've made changes.
  20.     
  21.     More assertions than an email meeting with Brian Bechtel!
  22. */
  23.  
  24. /////////////////////////////////////////////////////////////////////
  25. // The OT debugging macros in <OTDebug.h> require this variable to
  26. // be set.
  27.  
  28. #ifndef qDebug
  29. #define qDebug    1
  30. #endif
  31.  
  32. /////////////////////////////////////////////////////////////////////
  33. // Determine whether this is going to be an instrumented build or not.
  34.  
  35. #ifndef INSTRUMENTATION_ACTIVE
  36.     #define INSTRUMENTATION_ACTIVE 0
  37. #else
  38.     #define INSTRUMENTATION_ACTIVE 1
  39. #endif
  40.  
  41. /////////////////////////////////////////////////////////////////////
  42. // Pick up all the standard OT module stuff.
  43.  
  44. #include <OpenTptModule.h>
  45.  
  46. /////////////////////////////////////////////////////////////////////
  47. // Pick up the OTDebugBreak and OTAssert macros.
  48.  
  49. #include <OTDebug.h>
  50.  
  51. /////////////////////////////////////////////////////////////////////
  52. // Pick up all the standard TPI constants and structures.
  53.  
  54. #include <tihdr.h>
  55.  
  56. /////////////////////////////////////////////////////////////////////
  57. // You've gotta wonder why noCacheMask is only declared in <FSM.h>!
  58.  
  59. #include <FSM.h>
  60.  
  61. /////////////////////////////////////////////////////////////////////
  62. // Pick up Instrumentation SDK stuff.  We only do this if we're
  63. // actually instrumenting, so you don't even have to have the SDK
  64. // to compile the non-instumented version of the code.  If we're
  65. // instrumenting, we compile a bunch of bogus macros that generally
  66. // compile to nothing.
  67.  
  68. #if INSTRUMENTATION_ACTIVE
  69.     #include <InstrumentationMacros.h>
  70. #else
  71.     #define TRACE_SETUP        long __junk
  72.     #define LOG_ENTRY(n)    if (0) { __junk ; }
  73.     #define LOG_EXIT        if (0) { __junk ; }
  74. #endif
  75.  
  76. /////////////////////////////////////////////////////////////////////
  77. // Pick up our module specific data structures, specifically
  78. // the AF_FILESPEC address format.
  79.  
  80. #include "TPIFile.h"
  81.  
  82. /////////////////////////////////////////////////////////////////////
  83. // OTDebugStr is not defined in any OT header files, but it is
  84. // exported by the libraries, so we define the prototype here.
  85.  
  86. extern pascal void OTDebugStr(const char* str);
  87.  
  88. /////////////////////////////////////////////////////////////////////
  89.  
  90. #if INSTRUMENTATION_ACTIVE
  91.  
  92.     // If we're instrumenting, do a special hack to allow us
  93.     // to see calls to the File Manager.  We declare stub
  94.     // routines with a trailing "X" for each File Manager
  95.     // routine we call, and then macro define the corresponding
  96.     // identifiers to expand out to the "X" routine.
  97.     //
  98.     // In general you don't have to do this sort of rubbish
  99.     // when instrumenting your code.  The Instrumentation SDK
  100.     // describes how you can use MrProf to do an equivalent
  101.     // thing automagically.  I tried that and TPIFile started
  102.     // crashing unexpectedly.  I would love to know why, but
  103.     // alas I didn't have enough time to debug it.  Instead,
  104.     // I did this hack.  *sigh*
  105.  
  106.     static pascal OSErr PBHOpenAsyncX(HParmBlkPtr paramBlock)
  107.     {
  108.         TRACE_SETUP;
  109.         OSErr result;
  110.         
  111.         LOG_ENTRY( "TPIFile:PBHOpenAsync" );
  112.         result = PBHOpenAsync(paramBlock);
  113.         LOG_EXIT;
  114.         return (result);
  115.     }
  116.  
  117.     static pascal OSErr PBReadAsyncX(ParmBlkPtr paramBlock)
  118.     {
  119.         TRACE_SETUP;
  120.         OSErr result;
  121.         
  122.         LOG_ENTRY( "TPIFile:PBReadAsync" );
  123.         result = PBReadAsync(paramBlock);
  124.         LOG_EXIT;
  125.         return (result);
  126.     }
  127.  
  128.     static pascal OSErr PBCloseAsyncX(ParmBlkPtr paramBlock)
  129.     {
  130.         TRACE_SETUP;
  131.         OSErr result;
  132.         
  133.         LOG_ENTRY( "TPIFile:PBCloseAsync" );
  134.         result = PBCloseAsync(paramBlock);
  135.         LOG_EXIT;
  136.         return (result);
  137.     }
  138.  
  139.     #define PBHOpenAsync    PBHOpenAsyncX
  140.     #define PBReadAsync        PBReadAsyncX
  141.     #define PBCloseAsync    PBCloseAsyncX
  142.  
  143. #endif
  144.  
  145. /////////////////////////////////////////////////////////////////////
  146.  
  147. static mblk_t* qmi_tpi_data_ind(mblk_t* trailer_mp, int flags, long type)
  148.     // I found that mi_tpi_data_ind is not exported by the OT libraries,
  149.     // so I simply include my own verison here.
  150. {
  151.     mblk_t* mp;
  152.     
  153.     mp = mi_tpi_data_req(trailer_mp, flags, type);
  154.     if (mp)
  155.         ((struct T_data_ind *)mp->b_rptr)->PRIM_type = T_DATA_IND;
  156.     return mp;
  157. }
  158.  
  159. /////////////////////////////////////////////////////////////////////
  160.  
  161. static Boolean IsReadQ(queue_t* q)
  162.     // Returns true if q is the read queue of a queue pair.
  163. {
  164.     return ( (q->q_flag & QREADR) != 0 );
  165. }
  166.  
  167. static Boolean IsWriteQ(queue_t* q)
  168.     // Returns true if q is the write queue of a queue pair.
  169. {
  170.     return ( (q->q_flag & QREADR) == 0 );
  171. }
  172.  
  173. /////////////////////////////////////////////////////////////////////
  174.  
  175. // kDataBufferSize is the number of bytes we read of the file
  176. // at a time.  If you have a file that's larger than kDataBufferSize,
  177. // it gets sent upstream in kDataBufferSize chunks.  The number was
  178. // chosen because I know that OT doesn't like you asking for big
  179. // large individual memory buffers.  Remember, OT's memory manager
  180. // is designed for networking, and optimised for buffers that are
  181. // the size of your typical network packet.
  182.  
  183. enum {
  184.     kDataBufferSize = 2048
  185. };
  186.  
  187. /////////////////////////////////////////////////////////////////////
  188. // Per-Stream information
  189.  
  190. // This structure is used to hold the per-stream data for the module.
  191. // While module's can use normal global variables to store real globals,
  192. // they must maintain their own per-stream data structures.  I use
  193. // mi_open_comm to allocate this data structure when the stream is
  194. // opened.  mi_open_comm stores the address of this data structure in the
  195. // read and write queue's q_ptr field, so the rest of the code
  196. // can get to it by calling the GetPerStreamData function.
  197.  
  198.     // Due to an intensely annoying inability to distinguish three different
  199.     // states in DoDisconnectRequestAck, I need to define a sub-state variable
  200.     // that distinguishes between the possible ways to get to DoDisconnectRequestAck
  201.     // when the stream is in state TS_WACK_DREQ6.
  202.  
  203.     typedef enum {
  204.         WSS_OPEN = 0,            // Got to DoDisconnectRequestAck by getting a T_DISCON_REQ while in TS_WCON_CREQ.
  205.         WSS_ISSUE_MP            // Got to DoDisconnectRequestAck by calling CloseFileAndPostMessage.
  206.     } WackSubStateType;
  207.  
  208. struct PerStreamData
  209. {
  210.     OSType                 magic;                // kTPIFilePerStreamDataMagic = 'ESK0' for debugging
  211.  
  212.     long                   currentState;        // State of the TPI module, TS_UNBND etc
  213.  
  214.     WackSubStateType    wackSubstate;        // Sub-state while in TS_WACK_DREQ6, WSS_OPEN etc
  215.  
  216.     queue_t             *readQueue;            // Read queue for this stream.  Used by the
  217.                                             // the ioCompletion routine to find the queue.
  218.  
  219.     mblk_t                 *currentMessage;    // Current message pending ioCompletion.
  220.                                             // When the CustomIOCompletion routine fires,
  221.                                             // it puts this message on the above queue.
  222.  
  223.     HParamBlockRec        fileParamBlock;        // Current pending IOParamBlock
  224.  
  225.     FSSpec                 fileSpec;            // The file we're connected to.
  226.  
  227.     short                 fileRefNum;            // The fileRefNum if the file is open.
  228.  
  229.     Boolean                detached;            // Whether this stream has been detached.
  230.                                             // Streams get detached when they are closed
  231.                                             // but there is outstanding asynchronous I/O
  232.                                             // which we have to wait to complete before
  233.                                             // we can dispose of this data structure.
  234. };
  235. typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
  236.  
  237.  
  238. static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
  239.     // You can pass both the read or the write queue to this routine
  240.     // because mi_open_comm sets up both q_ptr's to point to the
  241.     // queue local data.
  242.     //
  243.     // Note that, in order to avoid the overhead of a function call,
  244.     // you would normally use inline code (or a macro)
  245.     // to get your per-stream data instead of using a separate function.
  246.     // However I think the separate function makes things clearer.
  247.     // I also acts as a central bottleneck for my debugging code.
  248.     //
  249.     // Environment: any standard STREAMS entry point
  250. {
  251.     PerStreamDataPtr streamData;
  252.     
  253.     streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
  254.  
  255.     OTAssert("GetPerStreamData: what streamData", streamData != nil);
  256.     OTAssert("GetPerStreamData: Bad magic", streamData->magic == kTPIFilePerStreamDataMagic);
  257.     
  258.     return (streamData);
  259. }
  260.  
  261. // mi_open_comm and mi_close_comm (and also mi_detach and mi_close_detached)
  262. // use this global to store the list of open streams to this module.
  263.  
  264. static char* gModuleList = nil;
  265.  
  266. // A UPP for CustomIOCompletion.
  267.  
  268. static IOCompletionUPP gCustomIOCompletionUPP = nil;
  269.  
  270. /////////////////////////////////////////////////////////////////////
  271.  
  272. static void CloseFileAndPostMessage(queue_t* q, mblk_t* mp)
  273.     // This routine is called to close the open file associated
  274.     // with the stream the given queue (which is the read-side
  275.     // queue).  mp is either nil, or the message that should be
  276.     // posted to the read-side service routine when the close is
  277.     // complete.
  278.     // 
  279.     // Environment: read service routine
  280.     // Environment: Deferred Task (called by CleanUpPerStreamData)
  281.     // Environment: close entry point
  282. {
  283.     TRACE_SETUP;
  284.     PerStreamDataPtr streamData;
  285.     
  286.     LOG_ENTRY( "TPIFile:CloseFileAndPostMessage" );
  287.     OTAssert("CloseFileAndPostMessage: Not the read queue", IsReadQ(q) );
  288.     
  289.     streamData = GetPerStreamData(q);
  290.     OTAssert("CloseFileAndPostMessage: Already have a current message", streamData->currentMessage == nil);
  291.     OTAssert("CloseFileAndPostMessage: File isn't open", streamData->fileRefNum != 0);
  292.  
  293.     streamData->currentMessage = mp;
  294.  
  295.     // Set up the parameter block for the _Close.
  296.         
  297.     streamData->fileParamBlock.ioParam.ioRefNum = streamData->fileRefNum;
  298.     streamData->fileParamBlock.ioParam.ioCompletion = gCustomIOCompletionUPP;
  299.     
  300.     // This is critical.  Set our copy of file reference number to 0 so
  301.     // that DoDisconnectRequestAck knows that it's the second time it's been called.
  302.     
  303.     streamData->fileRefNum = 0;
  304.     
  305.     // Start the close operation.
  306.     
  307.     (void) PBCloseAsync( (ParmBlkPtr) &streamData->fileParamBlock);
  308.  
  309.     // ... continue in CustomIOCompletion... TPIFileReadService... ( DoDisconnectRequestAck or DoDisconnectIndication)...
  310.     LOG_EXIT;
  311. }
  312.  
  313. /////////////////////////////////////////////////////////////////////
  314.  
  315. static Boolean CleanUpPerStreamData(PerStreamDataPtr streamData)
  316.     // This routine is called to clean up the per-stream data for this
  317.     // stream.  The routine returns true if the stream is clean (ie
  318.     // the memory can be disposed of), or false if there is an
  319.     // async close operation that means the stream must be left lying around.
  320.     // If it returns false, the rest of the module organises to call
  321.     // it repeatably until it returns true.
  322.     //
  323.     // Environment: Deferred Task (scheduled by CustomIOCompletion)
  324.     // Environment: close entry point
  325.     // Environment: TerminateStreamModule
  326. {
  327.     TRACE_SETUP;
  328.     Boolean result;
  329.     
  330.     LOG_ENTRY( "TPIFile:CleanUpPerStreamData" );
  331.     
  332.     OTAssert("CleanUpPerStreamData: Already have a current message", streamData->currentMessage == nil);
  333.     
  334.     // Test to see whether the file is currently open.
  335.     
  336.     if ( (streamData->fileRefNum != 0) || (streamData->fileParamBlock.ioParam.ioResult > noErr) ) {
  337.  
  338.         // The file is open, or the close is still in progress.  If the close isn't
  339.         // in progress, start one.  Regardless, return false to let the caller know
  340.         // we can't be killed yet.
  341.         
  342.         if ( streamData->fileParamBlock.ioParam.ioResult <= noErr ) {
  343.             // OTDebugBreak("CleanUpPerStreamData: Executing the especially hard case");
  344.             CloseFileAndPostMessage(streamData->readQueue, nil);
  345.         }
  346.  
  347.         result = false;
  348.     } else {
  349.     
  350.         // The file is closed.  No other clean up required, but if streamData
  351.         // contained pointers to other data structures, this is where we would
  352.         // dispose them.
  353.         
  354.         result = true;
  355.     }
  356.     
  357.     LOG_EXIT;
  358.     return (result);
  359. }
  360.  
  361. /////////////////////////////////////////////////////////////////////
  362.  
  363. static long gCleanUpAnyDetachedStreamsTaskID = 0;
  364.  
  365. static pascal void CleanUpAnyDetachedStreams(void* junkArg)
  366.     // Loop through all of the streams looking for detached ones.
  367.     // When we find a detached stream, attempt to close it by
  368.     // calling CleanUpPerStreamData.
  369.     //
  370.     // When the ioCompletion fires but finds it hasn't got a
  371.     // message to work with (because the stream has closed) it
  372.     // schedules this deferred task to run and actually kill
  373.     // the stream.
  374.     //
  375.     // TerminateStreamModule also calls this routine
  376.     // to kill any remaining streams before the module quits.
  377.     //
  378.     // Environment: Deferred Task (scheduled by CustomIOCompletion)
  379.     // Environment: TerminateStreamModule
  380. {
  381.     TRACE_SETUP;
  382.     #pragma unused(junkArg)
  383.     PerStreamDataPtr thisStreamData;
  384.     
  385.     LOG_ENTRY( "TPIFile:CleanUpAnyDetachedStreams" );
  386.  
  387.     // OTDebugBreak("CleanUpAnyDetachedStreams: Entering");
  388.     
  389.     // Loop through each of the streams...
  390.     
  391.     thisStreamData = (PerStreamDataPtr) gModuleList;
  392.     while (thisStreamData != nil) {
  393.         if (thisStreamData->detached && CleanUpPerStreamData( thisStreamData ) ) {
  394.             
  395.             // Yes're allowed to kill this detached stream.
  396.         
  397.             mi_close_detached(&gModuleList, (char *) thisStreamData);
  398.             
  399.             // Resume the search at the beginning of the stream list because
  400.             // a) I don't know the exact ordering that mi_open_comm uses to
  401.             //    create the stream list, so I'll just do the entire thing again.
  402.             // b) There's a possibility that hardware interrupts are detaching
  403.             //    streams while we're working here, so we might as well pick
  404.             //    them up in this pass.
  405.             
  406.             thisStreamData = (PerStreamDataPtr) gModuleList;
  407.  
  408.         } else {
  409.         
  410.             // This stream is not detached or not eligible for closing, continue
  411.             // on with the next one.
  412.             thisStreamData = (PerStreamDataPtr) mi_next_ptr( (char *) thisStreamData );
  413.         }
  414.         
  415.     }
  416.     
  417.     // You might think that we can assert that we've mi_close_detached
  418.     // at least one stream here, but that may not be true.  Imagine this
  419.     // scenario:
  420.     //
  421.     // 1. CustomIOCompletion fires, marks stream detached, schedules
  422.     //    CleanUpAnyDetachedStreams
  423.     // 2. CleanUpAnyDetachedStreams runs, finds marked stream, 
  424.     //    and calls mi_close_detached on it.  It then resumes its
  425.     //    search at the beginning of the stream list.
  426.     // 3. CustomIOCompletion fires again, for a different stream.
  427.     //    It marks the stream detached and schedules
  428.     //    CleanUpAnyDetachedStreams again.  This works because
  429.     //    OTScheduleDeferredTask considers a running task (ie
  430.     //    CleanUpAnyDetachedStreams) to not be scheduled, so it
  431.     //    will schedule it again.
  432.     // 4. The first instance of CleanUpAnyDetachedStreams continues
  433.     //    running and picks up the second marked stream, which it closes.
  434.     // 5. The second instance of CleanUpAnyDetachedStreams runs,
  435.     //    and can't find a stream to close.
  436.     //
  437.     // The fact that OTScheduleDeferredTask will reschedule a running
  438.     // task is *good* because, although it means there's a possibility
  439.     // that we'll run CleanUpAnyDetachedStreams unnecessarily, it also
  440.     // guarantees that we'll never leave a dangling detached stream.
  441.     
  442.     LOG_EXIT;
  443. }
  444.  
  445. /////////////////////////////////////////////////////////////////////
  446.  
  447. static pascal void CustomIOCompletion(ParmBlkPtr paramBlock)
  448.     // This function is the ioCompletion routine we use for all of
  449.     // our File Manager operations.  Because it operates at hardware
  450.     // interrupt level, it is not synchronised with the rest of STREAMS
  451.     // and so we have to be careful that we only call STREAMS routines
  452.     // that are accessible from hardware interrupt level.  This list
  453.     // of routines is given in the "Open Tpt Module Dev. Note".
  454.     // 
  455.     // The routine performs two different functions based on whether
  456.     // currentMessage is nil or not.  If currentMessage is not nil,
  457.     // the routine simply puts that message on the stream's read-side
  458.     // queue.  This schedules the TPIFileReadService, which continues
  459.     // the processing of the operation based on the type of the message.
  460.     // The person who called the File Manager will have set currentMessage
  461.     // up appropriately.
  462.     //
  463.     // If currentMessage is nil, the stream was closed before the
  464.     // File Manager ioCompletion routine ran.  We have to take special
  465.     // care to clean up this stream correctly by scheduling the
  466.     // CleanUpAnyDetachedStreams deferred task.
  467.     //
  468.     // In looking at trace logs it seems that PBCloseAsync pretty much
  469.     // always operates synchronously, so this second case is most probably
  470.     // not well tested.
  471.     //
  472.     // Environment: Hardware Interrupt (ioCompletion from File Manager)
  473. {
  474.     TRACE_SETUP;
  475.     PerStreamDataPtr streamData;
  476.     mblk_t *mp;
  477.     
  478.     LOG_ENTRY( "TPIFile:CustomIOCompletion" );
  479.  
  480.     // Tell OT we're operating at hardware interrupt level.
  481.     
  482.     OTEnterInterrupt();
  483.     
  484.     // Get the per-stream data associated with this File Manager
  485.     // operation using the standard "offset from the ParamBlockRec"
  486.     // technique.
  487.     
  488.     streamData = (PerStreamDataPtr) ( ((UInt8 *) paramBlock) - offsetof(PerStreamData, fileParamBlock) );
  489.     OTAssert("CustomIOCompletion: Bad magic", streamData->magic == kTPIFilePerStreamDataMagic);
  490.     // OTAssert("CustomIOCompletion: This is not wrong, merely interesting", streamData->currentMessage != nil);
  491.     
  492.     mp = streamData->currentMessage;
  493.     
  494.     if (mp == nil) {
  495.         
  496.         // The stream was closed before the ioCompletion routine could fire.
  497.         // Schedule our deferred task to clean up the wreckage.
  498.         
  499.         OTAssert("CustomIOCompletion: Clean up deferred task not created", gCleanUpAnyDetachedStreamsTaskID != 0);
  500.         
  501.         streamData->detached = true;
  502.         
  503.         OTScheduleDeferredTask(gCleanUpAnyDetachedStreamsTaskID);
  504.         
  505.         // ... continue in CleanUpAnyDetachedStreams...
  506.         
  507.     } else {
  508.  
  509.         streamData->currentMessage = nil;
  510.         
  511.         // The stream is still a going proposition.  Put
  512.         // the current message on our read-side queue,
  513.         // which has the effect of scheduling our read-side
  514.         // service routine TPIFileReadService which will
  515.         // forward the message upstream.
  516.  
  517.         putq(streamData->readQueue, mp);
  518.  
  519.         // ... continue in TPIFileReadService ...
  520.     }
  521.     
  522.     OTLeaveInterrupt();
  523.     
  524.     LOG_EXIT;
  525. }
  526.  
  527. /////////////////////////////////////////////////////////////////////
  528. // Open routine
  529.  
  530. static int TPIFileOpen(queue_t* rdq, dev_t* dev, int flag, int sflag, cred_t* creds)
  531.     // This routine is called by STREAMS when a new stream is connected to
  532.     // our module.  The bulk of the work here is done by the Mentat helper
  533.     // routine mi_open_comm.
  534.     //
  535.     // Environment: standard STREAMS entry point
  536. {
  537.     TRACE_SETUP;
  538.     int err;
  539.     PerStreamDataPtr streamData;
  540.  
  541.     LOG_ENTRY( "TPIFile:TPIFileOpen" );
  542.     
  543.     OTAssert("TPIFileOpen: Not the read queue", IsReadQ(rdq) );
  544.  
  545.     err = noErr;
  546.     
  547.     // If we already have per-stream data for this stream, the stream is being reopened.
  548.     // In that case, we can just return.
  549.     // Note that we can't call GetPerStreamData because it checks that streamData is not nil.
  550.     
  551.     if ( rdq->q_ptr != nil ) {
  552.         goto done;
  553.     }
  554.  
  555.     // Make sure we're being opened properly -- because we're a driver we
  556.     // don't allow a "module" open.
  557.     
  558.     if ( err == noErr && sflag == MODOPEN ) {
  559.         err = ENXIO;
  560.     }
  561.     
  562.     // Use the mi_open_comm routine to allocate our per-stream data.  Then
  563.     // zero out the entire per-stream data record and fill out the fields
  564.     // we're going to need.
  565.     
  566.     if (err == noErr) {
  567.         err = mi_open_comm(&gModuleList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
  568.         if ( err == noErr ) {
  569.             // Note that we can't call GetPerStreamData because the magic is not set up yet.
  570.             streamData = (PerStreamDataPtr) rdq->q_ptr;
  571.             
  572.             OTMemzero(streamData, sizeof(PerStreamData));
  573.             
  574.             streamData->magic = kTPIFilePerStreamDataMagic;
  575.             streamData->currentState = TS_UNBND;
  576.             streamData->readQueue = rdq;
  577.         }
  578.     }
  579.  
  580. done:
  581.     LOG_EXIT;
  582.     return (err);
  583. }
  584.  
  585. /////////////////////////////////////////////////////////////////////
  586. // Close routine
  587.  
  588. static int TPIFileClose(queue_t* rdq, int flags, cred_t* credP)
  589.     // This routine is called by STREAMS when a stream is being
  590.     // disconnected from our driver (ie closed).  The operation of this
  591.     // routine is complicated by one important fact: because we
  592.     // call File Manager asynchronously, there may be a File Manager
  593.     // I/O operation in progress when the stream is closed.  If this
  594.     // is the case, must use a special technique (mi_detach) to 
  595.     // detach our per-stream data from the actual stream, and 
  596.     // then organise to clean up this data at some later stage.
  597.     //
  598.     // Environment: standard STREAMS entry point
  599. {
  600.     TRACE_SETUP;
  601.     #pragma unused(flags)
  602.     #pragma unused(credP)
  603.     PerStreamDataPtr streamData;
  604.     mblk_t *oldCurrentMessage;
  605.  
  606.     LOG_ENTRY( "TPIFile:TPIFileClose" );
  607.     OTAssert("TPIFileClose: Not the read queue", IsReadQ(rdq) );
  608.  
  609.     streamData = GetPerStreamData(rdq);
  610.     
  611.     // Be very careful here.  streamData->currentMessage is also modified by
  612.     // the CustomIOCompletion routine, which (unlike the rest of our
  613.     // module) is not synchronised with this routine.  So we must use
  614.     // atomic operations to do this change streamData->currentMessage
  615.     // to make sure that either we close the stream or we detach the
  616.     // stream and CustomIOCompletion uses a deferred task to close it.
  617.     //
  618.     // Note that CustomIOCompletion does not have to use an atomic
  619.     // operation to modify streamData->currentMessage because it can
  620.     // interrupt us but we can't interrupt it.
  621.     
  622.     do {
  623.         oldCurrentMessage = streamData->currentMessage;
  624.     } while ( ! OTCompareAndSwapPtr(oldCurrentMessage, nil, &streamData->currentMessage) ); 
  625.     
  626.     if ( oldCurrentMessage != nil ) {
  627.  
  628.         // The ioCompletion routine hasn't fired yet, and if it does
  629.         // we've atomically guaranteed that it will know that this
  630.         // queue has been detached.  So we detach the queue and
  631.         // wait for the completion routine to schedule our deferred
  632.         // task.
  633.         
  634.         // OTDebugBreak("TPIFileClose: Executing the hard case");
  635.         
  636.         freemsg(oldCurrentMessage);
  637.  
  638.         mi_detach(rdq, (char *) streamData);
  639.         
  640.         // ... continue in CustomIOCompletion...
  641.     
  642.     } else {
  643.  
  644.         if ( CleanUpPerStreamData( streamData ) ) {
  645.  
  646.             // The easy case.  There is no outstanding ioCompletion routine,
  647.             // and the file is closed, so we can just shut down this stream.
  648.         
  649.             (void) mi_close_comm(&gModuleList, rdq);
  650.             
  651.         } else {
  652.         
  653.             // CleanUpPerStreamData has scheduled an async _Close,
  654.             // detach the stream and wait for it.
  655.         
  656.             mi_detach(rdq, (char *) streamData);
  657.  
  658.             // ... continue in CustomIOCompletion...
  659.         }
  660.  
  661.     }
  662.  
  663.     LOG_EXIT;
  664.     return (0);
  665. }
  666.  
  667. /////////////////////////////////////////////////////////////////////
  668.  
  669. enum {
  670.     kNoPrimitive = -1
  671. };
  672.  
  673. static long GetPrimitive(mblk_t* mp)
  674.     // GetPrimitive gets the TPI primitive out of a message block.
  675.     // It returns kNoPrimitive if the message block is of the wrong
  676.     // type or there is no TPI primitive.
  677.     //
  678.     // Environment: any standard STREAMS entry point
  679. {
  680.     if ((mp->b_datap->db_type == M_PROTO || mp->b_datap->db_type == M_PCPROTO) && MBLK_SIZE(mp) >= sizeof(long) ) {
  681.         return ( ( (union T_primitives*) mp->b_rptr)->type );
  682.     } else {
  683.         return ( kNoPrimitive );
  684.     }
  685. }
  686.  
  687. /////////////////////////////////////////////////////////////////////
  688.  
  689. static void DoInfoRequest(queue_t* q, mblk_t* mp)
  690.     // Handle a T_INFO_REQ TPI message by responding with
  691.     // a T_INFO_ACK message.
  692.     //
  693.     // Environment: write put routine
  694. {
  695.     TRACE_SETUP;
  696.     T_info_ack *infoAck;
  697.     PerStreamDataPtr streamData;
  698.  
  699.     LOG_ENTRY( "TPIFile:DoInfoRequest" );
  700.     OTAssert("DoInfoRequest: Not the write queue", IsWriteQ(q) );
  701.  
  702.     streamData = GetPerStreamData(q);
  703.     
  704.     // Allocate the T_INFO_ACK message, reusing mp if possible.
  705.     
  706.     mp = mi_tpi_ack_alloc(mp, sizeof(T_info_ack), T_INFO_ACK);
  707.     OTAssert("mi_tpi_ack_alloc failed", mp != nil );
  708.     
  709.     infoAck = (T_info_ack *) mp->b_rptr;
  710.     
  711.     // Fill out infoAck.  Note that we do not say we support
  712.     // orderly release!
  713.     
  714.     infoAck->TSDU_size = 0;
  715.     infoAck->ETSDU_size = T_INVALID;
  716.     infoAck->CDATA_size = T_INVALID;
  717.     infoAck->DDATA_size = T_INVALID;
  718.     infoAck->ADDR_size = T_INFINITE;
  719.     infoAck->OPT_size = T_INVALID;
  720.     infoAck->TIDU_size = T_INFINITE;
  721.     infoAck->SERV_type = T_COTS;
  722.     infoAck->CURRENT_state = streamData->currentState;
  723.     infoAck->PROVIDER_flag = 0;
  724.     
  725.     (void) qreply(q, mp);
  726.     LOG_EXIT;
  727. }
  728.  
  729. /////////////////////////////////////////////////////////////////////
  730.  
  731. static void ReplyWithErrorAck(queue_t* q, mblk_t* mp, long tpiError, long unixError)
  732.     // A simple routine which replies to a TPI message with a T_ERROR_ACK
  733.     // message containing the error codes tpiError and unixError.
  734.     //
  735.     // Environment: write put routine
  736. {
  737.     TRACE_SETUP;
  738.     
  739.     LOG_ENTRY( "TPIFile:ReplyWithErrorAck" );
  740.     OTAssert("ReplyWithErrorAck: Not the write queue", IsWriteQ(q) );
  741.  
  742.     mp = mi_tpi_err_ack_alloc(mp, tpiError, unixError);
  743.     OTAssert("mi_tpi_err_ack_alloc failed", mp != nil );
  744.     (void) qreply(q, mp);
  745.     LOG_EXIT;
  746. }
  747.  
  748. /////////////////////////////////////////////////////////////////////
  749.  
  750. static void DoBindRequest(queue_t* q, mblk_t* mp)
  751.     // Handle a T_BIND_REQ message by changing to the bound state
  752.     // and replying with a T_BIND_ACK message.
  753.     //
  754.     // Environment: write put routine
  755. {
  756.     TRACE_SETUP;
  757.     T_bind_req *bindReq;
  758.     T_bind_ack *bindAck;
  759.     PerStreamDataPtr streamData;
  760.  
  761.     LOG_ENTRY( "TPIFile:DoBindRequest" );
  762.     OTAssert("DoBindRequest: Not the write queue", IsWriteQ(q) );
  763.  
  764.     streamData = GetPerStreamData(q);
  765.     
  766.     // Check whether we're in the right state.
  767.     if (streamData->currentState != TS_UNBND) {
  768.         ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  769.         goto done;
  770.     }
  771.     
  772.     // Check the bind parameters.
  773.     bindReq = (T_bind_req *) mp->b_rptr;
  774.     
  775.     if (bindReq->CONIND_number != 0 || bindReq->ADDR_length != 0) {
  776.         ReplyWithErrorAck(q, mp, TNOADDR, 0);
  777.         goto done;
  778.     }
  779.  
  780.     // All is cool, lets say we're bound.
  781.     mp = mi_tpi_ack_alloc(mp, sizeof(T_bind_ack), T_BIND_ACK);
  782.     OTAssert("mi_tpi_ack_alloc failed", mp != nil );
  783.     
  784.     bindAck = (T_bind_ack *) mp->b_rptr;
  785.     
  786.     bindAck->ADDR_length = 0;
  787.     bindAck->ADDR_offset = sizeof(T_bind_ack);
  788.     bindAck->CONIND_number = 0;
  789.     
  790.     // Switch to the bound state.
  791.     
  792.     streamData->currentState = TS_IDLE;
  793.     
  794.     qreply(q, mp);
  795.  
  796. done:
  797.     LOG_EXIT;
  798.     return;
  799. }
  800.  
  801. /////////////////////////////////////////////////////////////////////
  802.  
  803. static void DoConnectRequest(queue_t* q, mblk_t* mp)
  804.     // Handle a T_CONN_REQ message in two stages.  If the connect
  805.     // request contains bogus information, we immediately NAK
  806.     // it with a T_ERROR_ACK.  If the information in the connect
  807.     // request looks OK, we start the connection process (ie by
  808.     // calling PBHOpenAsync) and send up a T_OK_ACK to say that
  809.     // the connection is in progress.  When the async operation
  810.     // completes, the ioCompletion fires, calling CustomIOCompletion
  811.     // which in turn puts streamData->currentMessage on the read-side
  812.     // queue, which schedule TPIFileReadService, which calls
  813.     // DoConnectConfirm which finally creates the T_CONN_CON message.
  814.     //
  815.     // Environment: write put routine
  816. {
  817.     TRACE_SETUP;
  818.     T_conn_req *connReq;
  819.     FileSpecAddressPtr connAddr;
  820.     PerStreamDataPtr streamData;
  821.  
  822.     LOG_ENTRY( "TPIFile:DoConnectRequest" );
  823.     OTAssert("DoConnectRequest: Not the write queue", IsWriteQ(q) );
  824.  
  825.     streamData = GetPerStreamData(q);
  826.     
  827.     // Check whether we're in the right state.
  828.     if (streamData->currentState != TS_IDLE) {
  829.         ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  830.         goto done;
  831.     }
  832.  
  833.     // Check the connect parameters.
  834.     connReq = (T_conn_req *) mp->b_rptr;
  835.     
  836.     if (connReq->OPT_length != 0) {
  837.         ReplyWithErrorAck(q, mp, TBADOPT, 0);
  838.         goto done;
  839.     }
  840.     if (connReq->DEST_length != sizeof(FileSpecAddress)) {
  841.         ReplyWithErrorAck(q, mp, TBADADDR, 0);
  842.         goto done;
  843.     }
  844.     connAddr = (FileSpecAddressPtr) mi_offset_paramc(mp, connReq->DEST_offset, connReq->DEST_length);
  845.     if (connAddr->fAddressType != AF_FILESPEC) {
  846.         ReplyWithErrorAck(q, mp, TBADADDR, 0);
  847.         goto done;
  848.     }
  849.  
  850.     streamData->fileSpec = connAddr->fss;
  851.  
  852.     // Create the T_CONN_CON message for use by the ioCompletion routine.
  853.     
  854.     OTAssert("DoConnectRequest: Already have a current message", streamData->currentMessage == nil);
  855.     streamData->currentMessage = mi_tpi_conn_con(nil, (char *) connAddr, sizeof(FileSpecAddress), nil, 0);
  856.     OTAssert("DoConnectRequest: mi_tpi_conn_con failed", streamData->currentMessage != nil);
  857.  
  858.     // mi_tpi_conn_con fills in all the fields of the message, so we have nothing more to do.
  859.     
  860.     streamData->currentState = TS_WCON_CREQ;
  861.     
  862.     streamData->fileParamBlock.ioParam.ioVRefNum = streamData->fileSpec.vRefNum;
  863.     streamData->fileParamBlock.fileParam.ioDirID = streamData->fileSpec.parID;
  864.     streamData->fileParamBlock.ioParam.ioNamePtr = &streamData->fileSpec.name[0];
  865.     streamData->fileParamBlock.ioParam.ioPermssn = fsRdPerm;
  866.     streamData->fileParamBlock.ioParam.ioVersNum = 0;
  867.     streamData->fileParamBlock.ioParam.ioMisc = nil;
  868.     streamData->fileParamBlock.ioParam.ioCompletion = gCustomIOCompletionUPP;
  869.     (void) PBHOpenAsync(&streamData->fileParamBlock);
  870.     // Throw away error result from PBOpenAsync because File Manager will still call
  871.     //  our completion routine.
  872.     
  873.     // We've started the connection attempt, respond with a T_OK_ACK.
  874.     
  875.     mp = mi_tpi_ok_ack_alloc(mp);
  876.     OTAssert("mi_tpi_ok_ack_alloc failed", mp != nil );
  877.  
  878.     qreply(q, mp);    
  879.  
  880.     // ... continue in CustomIOCompletion... TPIFileReadService... DoConnectConfirm...
  881.  
  882. done:
  883.     LOG_EXIT;
  884.     return;    
  885. }
  886.  
  887. /////////////////////////////////////////////////////////////////////
  888.  
  889. static void DoDisconnectRequest(queue_t* q, mblk_t* mp)
  890.     // Handling a T_DISCON_REQ message is complicated by two
  891.     // factors: a) there are two possible states that the stream
  892.     // can be in and get a valid T_DISCON_REQ, and we have to
  893.     // handle both cases slightly differently, and b) there
  894.     // could be a pending asynchronous I/O operation for this
  895.     // stream, and we don't want it going upstream after we've
  896.     // disconnected.
  897.     //
  898.     // To handle the first difficulty, we have to case our
  899.     // behaviour on the current state of the stream.
  900.     // If the stream is in TS_WCON_CREQ (Waiting for Confirmation
  901.     // of Connection Request -- we were asked to disconnect
  902.     // while we were in the process of connecting), we switch
  903.     // to state TS_WACK_DREQ6.  If the state is TS_DATA_XFER
  904.     // (Data Transfer -- we were asked to disconnect while
  905.     // we are tranferring data), we switch to TS_WACK_DREQ9.
  906.     //
  907.     // This resulting state is important because DoDisconnectRequestAck
  908.     // uses it to determine the next state in the state machine.
  909.     //
  910.     // We can't just send the T_OK_ACK upstream from here because
  911.     // we most probably have an outstanding File Manager completion
  912.     // routine (and it's hard to tell because File Manager completions
  913.     // are not synchronised with STREAMS) for the _Read or _Open,
  914.     // and we don't want that to complete at an unexpected
  915.     // time.  In addition, there is no way of cancelling
  916.     // outstanding File Manager requests.
  917.     //
  918.     // In the case where there is a pending File Manager operation,
  919.     // we simply swap out the current message and replace it with
  920.     // our T_OK_ACK message.  This must be done atomically to ensure
  921.     // that the completion routine doesn't fire while we're doing it.
  922.     //
  923.     // In the case where there is no pending File Manager operation,
  924.     // we post the message to the read-side queue directly.  This
  925.     // especially important in the case when the stream in in
  926.     // TS_DATA_XFER, because the read-side queue may be descheduled
  927.     // because of flow control.  Fortunately the T_OK_ACK message
  928.     // is a high-priority message, so it will be processed anyway.
  929.     //
  930.     // Environment: write put routine
  931. {
  932.     TRACE_SETUP;
  933.     PerStreamDataPtr streamData;
  934.     mblk_t* oldCurrentMessage;
  935.  
  936.     LOG_ENTRY( "TPIFile:DoDisconnectRequest" );
  937.     // OTDebugBreak("DoDisconnectRequest: Disconnect requested");
  938.  
  939.     OTAssert("DoDisconnectRequest: Not the write queue", IsWriteQ(q) );
  940.  
  941.     streamData = GetPerStreamData(q);
  942.  
  943.     // Do the right thing depending on the current state.
  944.     
  945.     switch ( streamData->currentState ) {
  946.         case TS_WCON_CREQ:
  947.         case TS_DATA_XFER:
  948.         
  949.             // Set the new state appropriately, so that 
  950.             // DoDisconnectRequestAck knows what to do.
  951.  
  952.             if ( streamData->currentState == TS_WCON_CREQ ) {
  953.                 streamData->wackSubstate = WSS_OPEN;
  954.                 streamData->currentState = TS_WACK_DREQ6;
  955.             } else {
  956.                 streamData->currentState = TS_WACK_DREQ9;
  957.             }
  958.  
  959.             // Allocate an appropriate T_OK_ACK message.
  960.  
  961.             mp = mi_tpi_ok_ack_alloc(mp);
  962.             OTAssert("DoDisconnectRequest: mi_tpi_ok_ack_alloc failed", mp != nil );
  963.             
  964.             // Swap it into this stream's currentMessage field.
  965.  
  966.             oldCurrentMessage = streamData->currentMessage;
  967.             if ( (oldCurrentMessage != nil ) && 
  968.                         OTCompareAndSwapPtr(oldCurrentMessage, mp, &streamData->currentMessage) ) {
  969.                 // We successfully managed to swap the message into the
  970.                 // current message field.  The T_OK_ACK message will be
  971.                 // posted when the ioCompletion runs.
  972.                 
  973.                 freemsg(oldCurrentMessage);
  974.             } else {
  975.                 OTAssert("DoDisconnectRequest: Message unexpectedly appeared", streamData->currentMessage == nil);
  976.                 
  977.                 // There is either no outstanding I/O operation, or it fired
  978.                 // while we were looking at it.  We must do clever things!
  979.                 // namely putting our T_OK_ACK message on the read-side queue.
  980.                 //
  981.                 // Because this is a high-priority message it will be delivered
  982.                 // before the T_CONN_CON or T_DATA_IND messages that might have
  983.                 // been queued by the ioCompletion routine before it in the stream
  984.                 // read-side queue.
  985.                 
  986.                 putq( RD(q) , mp);
  987.  
  988.                 // btw We do not freemsg(oldCurrentMessage) because the interrupt
  989.                 // handler has already put it on our read-side queue.
  990.             }
  991.             
  992.             // ... continue in DoDisconnectRequestAck...
  993.  
  994.             break;
  995.  
  996.         default:
  997.             OTDebugBreak("DoDisconnectRequest: Out of state");
  998.             ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  999.             break;
  1000.     }
  1001.     LOG_EXIT;
  1002. }
  1003.  
  1004. /////////////////////////////////////////////////////////////////////
  1005.  
  1006. static void DoUnbindRequest(queue_t* q, mblk_t* mp)
  1007.     // Handle a T_UNBIND_REQ message.  TPI says that
  1008.     // we can only get these in the TS_IDLE idle state.
  1009.     // If that's the case, we simply change state to
  1010.     // TS_UNBND and reply with a T_OK_ACK.
  1011.     //
  1012.     // Environment: write put routine
  1013. {
  1014.     TRACE_SETUP;
  1015.     PerStreamDataPtr streamData;
  1016.  
  1017.     LOG_ENTRY( "TPIFile:DoUnbindRequest" );
  1018.     OTAssert("DoUnbindRequest: Not the write queue", IsWriteQ(q) );
  1019.  
  1020.     streamData = GetPerStreamData(q);
  1021.  
  1022.     // Check whether we're in the right state.
  1023.     if (streamData->currentState != TS_IDLE) {
  1024.         ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  1025.         goto done;
  1026.     }
  1027.  
  1028.     // No other parameters to check, so everything is cool.
  1029.     // We change state and ACK the request.
  1030.     
  1031.     streamData->currentState = TS_UNBND;
  1032.     
  1033.     mp = mi_tpi_ok_ack_alloc(mp);
  1034.     OTAssert("DoUnbindRequest: mi_tpi_ok_ack_alloc failed", mp != nil );
  1035.  
  1036.     qreply(q, mp);
  1037.  
  1038. done:
  1039.     LOG_EXIT;
  1040. }
  1041.  
  1042. /////////////////////////////////////////////////////////////////////
  1043.  
  1044. static void DoOptionManagementRequest(queue_t* q, mblk_t* mp)
  1045.     // We've told the world that we don't support option management
  1046.     // so just error any T_OPTMGMT_REQ messages.
  1047.     //
  1048.     // Environment: write put routine
  1049. {
  1050.     TRACE_SETUP;
  1051.     OTAssert("DoOptionManagementRequest: Not the write queue", IsWriteQ(q) );
  1052.  
  1053.     LOG_ENTRY( "TPIFile:DoOptionManagementRequest" );
  1054.     ReplyWithErrorAck(q, mp, TBADOPT, 0);
  1055.     LOG_EXIT;
  1056. }
  1057.  
  1058. /////////////////////////////////////////////////////////////////////
  1059.  
  1060. static void DoStreamFatalError(queue_t* q, mblk_t* mp)
  1061.     // Send up a stream-fatal M_ERROR message.  We do this in response
  1062.     // to messages that are just wrong for our type of stream.
  1063.     //
  1064.     // Environment: write put routine
  1065. {
  1066.     TRACE_SETUP;
  1067.     
  1068.     OTDebugBreak("DoStreamFatalError");
  1069.  
  1070.     LOG_ENTRY( "TPIFile:DoStreamFatalError" );
  1071.     OTAssert("DoStreamFatalError: Not the write queue", IsWriteQ(q) );
  1072.     OTAssert("DoStreamFatalError: mp is nil", mp != nil );
  1073.     freemsg(mp);
  1074.     
  1075.     // Allocate a 1 byte M_ERROR message containing EPROTO
  1076.     // and send it back up the stream.  This basically kills
  1077.     // the stream as far as the client is concerned.  Normally
  1078.     // you wouldn't do this except in dire circumstances.  I
  1079.     // do it when I find that someone has sent me the wrong
  1080.     // kind of message.  Normally the stream head should protect
  1081.     // you against this.
  1082.     
  1083.     mp = allocb(sizeof(UInt8), 0);
  1084.     OTAssert("DoStreamFatalError: allocb failed", mp != nil );
  1085.     OTAssert("DoStreamFatalError: allocb is not doing what we expect", mp->b_rptr == mp->b_wptr);
  1086.     
  1087.     mp->b_datap->db_type = M_ERROR;
  1088.     
  1089.     *((UInt8 *) (mp->b_wptr)) = EPROTO;
  1090.     mp->b_wptr = mp->b_rptr + 1;
  1091.  
  1092.     qreply(q, mp);
  1093.     LOG_EXIT;
  1094. }
  1095.  
  1096. /////////////////////////////////////////////////////////////////////
  1097.  
  1098. static void DoDataRequest(queue_t* q, mblk_t* headerBlock, mblk_t* dataBlock)
  1099.     // Handle a T_DATA_REQ message.  Well actually, we don't handle
  1100.     // them, so we kill the stream if we get one.
  1101.     //
  1102.     // headerBlock may be nil when this messages is caused by an M_DATA
  1103.     // message.
  1104.     //
  1105.     // Environment: write put routine
  1106. {
  1107.     TRACE_SETUP;
  1108.     mblk_t* mp;
  1109.  
  1110.     LOG_ENTRY( "TPIFile:DoDataRequest" );
  1111.  
  1112.     OTAssert("DoDataRequest: Not the write queue", IsWriteQ(q) );
  1113.  
  1114.     if ( headerBlock == nil ) {
  1115.         mp = dataBlock;
  1116.     } else {
  1117.         mp = headerBlock;
  1118.     }
  1119.     
  1120.     // We do not handle writing data to TPIFile streams.
  1121.     DoStreamFatalError(q, headerBlock);
  1122.     LOG_EXIT;
  1123. }
  1124.  
  1125. /////////////////////////////////////////////////////////////////////
  1126.  
  1127. static void DoFlushRequest(queue_t* q, mblk_t* mp)
  1128.     // Handle an M_FLUSH message as per "STREAMS Modules and Drivers", p8-12.
  1129.     //
  1130.     // Environment: write put routine
  1131. {    
  1132.     TRACE_SETUP;
  1133.     
  1134.     LOG_ENTRY( "TPIFile:DoFlushRequest" );
  1135.     OTAssert("DoFlushRequest: Not the write queue", IsWriteQ(q) );
  1136.  
  1137.     // If we've been asked to flush the write queue, just go and do it.
  1138.     
  1139.     if ( ( *(mp->b_rptr) & FLUSHW ) != 0 ) {
  1140.         flushq(q, FLUSHDATA);
  1141.     }
  1142.  
  1143.     // Check whether we've been asked to flush the read queue.
  1144.  
  1145.     if ( ( *(mp->b_rptr) & FLUSHR ) != 0 ) {
  1146.         
  1147.         // If so, clear the FLUSHW bit and send the flush
  1148.         // request back up stream.  We don't actually flush
  1149.         // the contents of the read queue because doing so 
  1150.         // might compromise the delicate state machine in
  1151.         // this module.  I need to investigate whether this
  1152.         // is a serious problem and, if it is, work out a fix.
  1153.  
  1154.         *(mp->b_rptr) &= ~FLUSHW;
  1155.         qreply(q, mp);
  1156.         
  1157.     } else {
  1158.  
  1159.         // We weren't asked to flush the read queue, so
  1160.         // we're done with this message.
  1161.  
  1162.         freemsg(mp);
  1163.     }
  1164.     LOG_EXIT;
  1165. }
  1166.  
  1167. /////////////////////////////////////////////////////////////////////
  1168. // Write-side put routine
  1169.  
  1170. static int TPIFileWritePut(queue_t* q, mblk_t* mp)
  1171.     // This routine is called by STREAMS when it has a message for our
  1172.     // module.  This routine is basically a big case statement that
  1173.     // dispatches to our various message handling routines.
  1174.     // 
  1175.     // Note that a production module would probably want to handle
  1176.     // the high frequency requests (like T_DATA_REQ) inline in this
  1177.     // routine for maximum speed, but this module is still in the
  1178.     // "make it work" stage.
  1179.     //
  1180.     // Environment: standard STREAMS entry point
  1181. {
  1182.     TRACE_SETUP;
  1183.     PerStreamDataPtr streamData;
  1184.     struct iocblk *iocblkPtr;
  1185.     
  1186.     LOG_ENTRY( "TPIFile:TPIFileWritePut" );
  1187.  
  1188.     OTAssert("TPIFileWritePut: Not the write queue", IsWriteQ(q) );
  1189.  
  1190.     streamData = GetPerStreamData(q);
  1191.  
  1192.     switch ( mp->b_datap->db_type ) {
  1193.         case M_IOCTL:
  1194.             // "STREAMS Modules and Drivers" p8-33 says: "A driver must process
  1195.             // an M_IOCTL message.  Otherwise, the Stream head blocks for an M_IOCNAK
  1196.             // or M_IOCACK until the timeout (potentially infinite) expires. If a driver
  1197.             // does not understand an ioctl, an M_IOCNAK message must be sent to upstream."
  1198.  
  1199.             // I'm not sure why we're allowed to reuse this datab without
  1200.             // check it for read-only, but everyone does so!
  1201.             
  1202.             mp->b_datap->db_type = M_IOCNAK;
  1203.             iocblkPtr = (struct iocblk *) mp->b_rptr;
  1204.             iocblkPtr->ioc_error = EINVAL;
  1205.  
  1206.             qreply(q, mp);
  1207.             break;
  1208.         
  1209.         case M_DATA:
  1210.             // "STREAMS Modules and Drivers", Appendix A-2, T_DATA_REQ (7tpi) says:
  1211.             // "The transport provider must also recognize a message of one or more
  1212.             // M_DATA message blocks without the leading M_PROTO message block as a 
  1213.             // T_DATA_REQ primitive. This message type will be initiated from the write 
  1214.             // (BA_OS) operating system service routine."
  1215.  
  1216.             DoDataRequest(q, nil, mp);
  1217.             break;
  1218.             
  1219.         case M_FLUSH:
  1220.             DoFlushRequest(q, mp);
  1221.             break;
  1222.  
  1223.         default:
  1224.             switch ( GetPrimitive(mp) ) {
  1225.                 case T_INFO_REQ:
  1226.                     DoInfoRequest(q, mp);
  1227.                     break;
  1228.                 case T_BIND_REQ:
  1229.                     DoBindRequest(q, mp);
  1230.                     break;
  1231.                 case T_CONN_REQ:
  1232.                     DoConnectRequest(q, mp);
  1233.                     break;
  1234.                 case T_DISCON_REQ:
  1235.                     DoDisconnectRequest(q, mp);
  1236.                     break;
  1237.                 case T_UNBIND_REQ:
  1238.                     DoUnbindRequest(q, mp);
  1239.                     break;
  1240.                 case T_OPTMGMT_REQ:
  1241.                     DoOptionManagementRequest(q, mp);
  1242.                     break;
  1243.                 case T_DATA_REQ:
  1244.                     DoDataRequest(q, mp, mp->b_cont);
  1245.                     break;
  1246.                 
  1247.                 // Standard TPI messages that are inappropriate.
  1248.                 case T_ORDREL_REQ:
  1249.                 case T_UNITDATA_REQ:
  1250.                 case T_EXDATA_REQ:
  1251.                 
  1252.                 // Extended TPI messages that are inappropriate.
  1253.                 case T_ADDR_REQ:
  1254.                 
  1255.                 // Transaction TPI messages that are inappropriate.
  1256.                 case T_UREQUEST_REQ:
  1257.                 case T_REQUEST_REQ:
  1258.                 case T_UREPLY_REQ:
  1259.                 case T_REPLY_REQ:
  1260.                 case T_CANCELREQUEST_REQ:
  1261.                 case T_CANCELREPLY_REQ:
  1262.  
  1263.                 // Mapper TPI messages that are inappropriate.
  1264.                 case T_REGNAME_REQ:
  1265.                 case T_DELNAME_REQ:
  1266.                 case T_LKUPNAME_REQ:
  1267.  
  1268.                     DoStreamFatalError(q, mp);
  1269.                     break;
  1270.  
  1271.                 case kNoPrimitive:
  1272.                 default:
  1273.                     // "STREAMS Modules and Drivers" p8-33 says: "Messages that are
  1274.                     // not understood by the driver should be freed."
  1275.                     
  1276.                     OTDebugBreak("TPIFileWritePut: Message not understood, freeing");
  1277.                     
  1278.                     freemsg(mp);
  1279.                     break;
  1280.             }
  1281.             break;
  1282.     }
  1283.     
  1284.     LOG_EXIT;
  1285.     
  1286.     return 0;
  1287. }
  1288.  
  1289. /////////////////////////////////////////////////////////////////////
  1290. // Read-side put routine
  1291.  
  1292. static int TPIFileReadPut(queue_t* q, mblk_t* mp)
  1293.     // Because we're a driver (ie at the end of the stream) this routine
  1294.     // should never be called by STREAMS.
  1295.     //
  1296.     // Environment: standard STREAMS entry point
  1297. {
  1298.     PerStreamDataPtr streamData;
  1299.     
  1300.     OTAssert("TPIFileReadPut: Not the read queue", IsReadQ(q) );
  1301.  
  1302.     streamData = GetPerStreamData(q);
  1303.  
  1304.     switch ( GetPrimitive(mp) ) {
  1305.         default:
  1306.             OTDebugBreak("TPIFileReadPut: Was called!");
  1307.             break;
  1308.     }
  1309.  
  1310.     return 0;
  1311. }
  1312.  
  1313. /////////////////////////////////////////////////////////////////////
  1314.  
  1315. static void StartDataIndication(queue_t* q)
  1316.     // StartDataIndication is called by either DoConnectionConfirm
  1317.     // or DoDataIndication to start a read request for a file.
  1318.     // The routine allocates a buffer for the T_DATA_IND message
  1319.     // and then starts an asynchronous File Manager _Read
  1320.     // request to read data into that buffer.  When the _Read
  1321.     // completes, CustomIOCompletion is called.  It puts 
  1322.     // the T_DATA_IND message on the read-side queue, which schedules
  1323.     // TPIFileReadService, which in turn calls DoDataIndication to
  1324.     // send the data upstream, and then calls StartDataIndication
  1325.     // again to start the next block.
  1326.     //
  1327.     // Environment: read service routine
  1328. {
  1329.     TRACE_SETUP;
  1330.     PerStreamDataPtr streamData;
  1331.     mblk_t* mp = nil;
  1332.     mblk_t* dataBuffer = nil;
  1333.  
  1334.     LOG_ENTRY( "TPIFile:StartDataIndication" );
  1335.  
  1336.     OTAssert("StartDataIndication: Not the read queue", IsReadQ(q) );
  1337.     
  1338.     streamData = GetPerStreamData(q);
  1339.     OTAssert("StartDataIndication: Already have a current message", streamData->currentMessage == nil);
  1340.     OTAssert("StartDataIndication: Wrong state", streamData->currentState == TS_DATA_XFER);
  1341.  
  1342.     // Create a data buffer for the data to be read.
  1343.     
  1344.     dataBuffer = allocb(kDataBufferSize, 0);
  1345.     OTAssert("StartDataIndication: allocb failed", dataBuffer != nil);
  1346.  
  1347.     // Create a T_DATA_IND message with that data buffer.
  1348.     
  1349.     mp = qmi_tpi_data_ind(dataBuffer, 0, 0);
  1350.     OTAssert("StartDataIndication: qmi_tpi_data_ind failed", mp != nil);
  1351.  
  1352.     // Remember the T_DATA_IND message as the current message, ie the
  1353.     // one that CustomIOCompletion will operate on.
  1354.     
  1355.     streamData->currentMessage = mp;
  1356.     
  1357.     // Start the File Manager read request.
  1358.  
  1359.     streamData->fileParamBlock.ioParam.ioRefNum = streamData->fileRefNum;
  1360.     streamData->fileParamBlock.ioParam.ioBuffer = (char *) dataBuffer->b_datap->db_base;
  1361.     streamData->fileParamBlock.ioParam.ioReqCount = dataBuffer->b_datap->db_lim - dataBuffer->b_datap->db_base;
  1362.     streamData->fileParamBlock.ioParam.ioPosMode = fsAtMark + noCacheMask;
  1363.     streamData->fileParamBlock.ioParam.ioPosOffset = 0;
  1364.     streamData->fileParamBlock.ioParam.ioCompletion = gCustomIOCompletionUPP;
  1365.     (void) PBReadAsync( (ParmBlkPtr) &streamData->fileParamBlock);
  1366.     // Throw away error result from PBReadAsync because File Manager will still call
  1367.     //  our completion routine.
  1368.     
  1369.     // ... continue in CustomIOCompletion... TPIFileReadService... DoDataIndication...
  1370.     LOG_EXIT;
  1371. }
  1372.  
  1373. /////////////////////////////////////////////////////////////////////
  1374.  
  1375. static void DoConnectConfirm(queue_t* q, mblk_t* mp)
  1376.     // Handle processing of a T_CONN_CON message.  This message was originally
  1377.     // created in DoConnectRequest and put into the per-stream data
  1378.     // variable currentMessage.  When the ioCompletion for the PBHOpenAsync
  1379.     // fired, the CustomIOCompletion fired and queued the message on to the
  1380.     // read-side queue.  The TPIFileReadService then called this routine.
  1381.     // 
  1382.     // Because the stream is still in TS_WCON_CREQ, the routine just sends
  1383.     // the T_CONN_CON upstream to indicate to the client that we have
  1384.     // a connection in place (or a T_DISCON_IND if the file failed to open).
  1385.     // 
  1386.     // Environment: read service routine
  1387. {
  1388.     TRACE_SETUP;
  1389.     PerStreamDataPtr streamData;
  1390.  
  1391.     LOG_ENTRY( "TPIFile:DoConnectConfirm" );
  1392.  
  1393.     OTAssert("DoConnectConfirm: Not the read queue", IsReadQ(q) );
  1394.     
  1395.     streamData = GetPerStreamData(q);
  1396.     OTAssert("DoConnectConfirm: Already have a current message", streamData->currentMessage == nil);
  1397.  
  1398.     OTAssert("DoConnectConfirm: Wrong state", streamData->currentState == TS_WCON_CREQ);
  1399.  
  1400.     // This is the final stage of the connection process.  If the
  1401.     // PBHOpenAsync failed, free the T_CONN_CON and send a T_DISCON_IND
  1402.     // upstream instead.
  1403.     // If the open succeed, start the process of data transfer and send
  1404.     // the T_CONN_CON upstream.
  1405.     
  1406.     if (streamData->fileParamBlock.fileParam.ioResult != noErr) {
  1407.         freemsg(mp);
  1408.         mp = mi_tpi_discon_ind(nil, streamData->fileParamBlock.fileParam.ioResult, -1);
  1409.         OTAssert("DoConnectConfirm: mi_tpi_discon_ind failed", mp != nil);
  1410.         streamData->currentState = TS_IDLE;
  1411.     } else {
  1412.         streamData->fileRefNum = streamData->fileParamBlock.fileParam.ioFRefNum;
  1413.         streamData->currentState = TS_DATA_XFER;
  1414.         StartDataIndication(q);
  1415.     }
  1416.     putnext(q, mp);
  1417.     LOG_EXIT;
  1418. }
  1419.  
  1420. /////////////////////////////////////////////////////////////////////
  1421.  
  1422. static void DoDataIndication(queue_t* q, mblk_t* mp)
  1423.     // Handle processing of a T_DATA_IND message.  This message was originally
  1424.     // created in StartDataIndication and put into the per-stream data
  1425.     // variable currentMessage.  When the ioCompletion for the PBReadAsync
  1426.     // fired, the CustomIOCompletion fired and queued the message on to the
  1427.     // read-side queue.  The TPIFileReadService then called this routine.
  1428.     //
  1429.     // Because the stream is still in TS_DATA_XFER, the routine just sends
  1430.     // the T_DATA_IND upstream to indicate to the client the arrival of
  1431.     // new data (or a T_DISCON_IND if the read failed).
  1432.     //
  1433.     // Environment: read service routine
  1434. {
  1435.     TRACE_SETUP;
  1436.     PerStreamDataPtr streamData;
  1437.     
  1438.     LOG_ENTRY( "TPIFile:DoDataIndication" );
  1439.  
  1440.     OTAssert("DoDataIndication: Not the read queue", IsReadQ(q) );
  1441.  
  1442.     streamData = GetPerStreamData(q);
  1443.     OTAssert("DoDataIndication: Already have a current message", streamData->currentMessage == nil);
  1444.  
  1445.     OTAssert("DoDataIndication: Wrong state", streamData->currentState == TS_DATA_XFER);
  1446.  
  1447.     // If we get an eofErr then check whether we read any data at all
  1448.     // -- if we did, return the data to the user and then start another
  1449.     // read attempt, which will fail and finally cause the T_DISCON_IND to be
  1450.     // issued
  1451.         
  1452.     if (streamData->fileParamBlock.ioParam.ioResult == eofErr &&     
  1453.             streamData->fileParamBlock.ioParam.ioActCount != 0) {
  1454.         streamData->fileParamBlock.ioParam.ioResult = noErr;
  1455.     }
  1456.  
  1457.     // If we get an error, free the T_DATA_IND and send a T_DISCON_IND upstream instead.
  1458.     
  1459.     if (streamData->fileParamBlock.ioParam.ioResult != noErr) {
  1460.         freemsg(mp);
  1461.         mp = mi_tpi_discon_ind(nil, streamData->fileParamBlock.ioParam.ioResult, -1);
  1462.         OTAssert("DoDataIndication: mi_tpi_discon_ind failed", mp != nil);
  1463.  
  1464.         CloseFileAndPostMessage(q, mp);
  1465.         
  1466.         // ... continue in CustomIOCompletion... TPIFileReadService... DoDisconnectIndication...
  1467.  
  1468.     } else {
  1469.         OTAssert("DoDataIndication: No data in data message", mp->b_cont != nil);
  1470.         mp->b_cont->b_rptr = mp->b_cont->b_datap->db_base;
  1471.         mp->b_cont->b_wptr = mp->b_cont->b_datap->db_base + streamData->fileParamBlock.ioParam.ioActCount;
  1472.         putnext(q, mp);
  1473.         StartDataIndication(q);
  1474.     }
  1475.  
  1476.     LOG_EXIT;
  1477. }
  1478.  
  1479. /////////////////////////////////////////////////////////////////////
  1480.  
  1481. static void DoDisconnectRequestAck(queue_t* q, mblk_t* mp)
  1482.     // This routine is called when a T_OK_ACK message shows up on
  1483.     // the read-side queue.  This message was put there
  1484.     // by DoDisconnectRequest, which also switched the state to either
  1485.     // TS_WACK_DREQ9 or TS_WACK_DREQ6 depending on the state that
  1486.     // the stream was in when the T_DISCON_REQ arrived.
  1487.     //
  1488.     // See the comments in each branch of the case statement for
  1489.     // how these different cases are handled.
  1490.     //
  1491.     // Environment: read service routine
  1492. {
  1493.     TRACE_SETUP;
  1494.     PerStreamDataPtr streamData;
  1495.  
  1496.     LOG_ENTRY( "TPIFile:DoDisconnectRequestAck" );
  1497.  
  1498.     OTAssert("DoDisconnectRequestAck: Not the read queue", IsReadQ(q) );
  1499.     
  1500.     OTAssert("DoDisconnectRequestAck: Unexpected message primitive", 
  1501.             ( (GetPrimitive(mp) == T_OK_ACK) && (( (struct T_ok_ack *) mp->b_rptr)->CORRECT_prim == T_DISCON_REQ) )
  1502.         );
  1503.  
  1504.     streamData = GetPerStreamData(q);
  1505.     OTAssert("DoDisconnectRequestAck: Already have a current message", streamData->currentMessage == nil);
  1506.  
  1507.     switch ( streamData->currentState ) {
  1508.         case TS_WACK_DREQ6:
  1509.             // We've been asked to disconnect during the connection process,
  1510.             // ie between when we got the T_CONN_REQ and when the PBHOpenAsync
  1511.             // completed.  In this case, we ACK the T_DISCON_REQ and go back to
  1512.             // TS_IDLE state.  Except it's a bit more complicated than that...
  1513.  
  1514.             switch ( streamData->wackSubstate ) {
  1515.             
  1516.                 case WSS_OPEN:
  1517.                     // This is the first time we've hit this routine.  Check to see
  1518.                     // whether the file opened successfully.  If it did, we have
  1519.                     // to organise to close it before sending up the T_OK_ACK.
  1520.                     // We switch the wackSubState to WSS_ISSUE_MP to ensure that,
  1521.                     // the next time we come to this routine, we know that we've
  1522.                     // already closed the file.
  1523.                 
  1524.                     OTAssert("DoDisconnectRequestAck: File should be closed", streamData->fileRefNum == 0);
  1525.                     if (streamData->fileParamBlock.fileParam.ioResult != noErr) {
  1526.                         // If we got an error from the PBHOpenAsync, the file is not open, so we can
  1527.                         // just ACK the disconnect request immediately.
  1528.                         
  1529.                         streamData->currentState = TS_IDLE;
  1530.                         putnext(q, mp);
  1531.                         
  1532.                     } else {
  1533.                         
  1534.                         // The file is now open, but we have to close it before we
  1535.                         // can ACK the disconnect request.  So start the async
  1536.                         // close request.
  1537.                         
  1538.                         streamData->fileRefNum = streamData->fileParamBlock.fileParam.ioFRefNum;
  1539.                         
  1540.                         streamData->wackSubstate = WSS_ISSUE_MP;
  1541.                         CloseFileAndPostMessage(q, mp);
  1542.                         // ... continue in CustomIOCompletion... TPIFileReadService... DoDisconnectRequestAck
  1543.                         // ie the WSS_ISSUE_MP branch of this case statement
  1544.                     }
  1545.                     break;
  1546.  
  1547.                 case WSS_ISSUE_MP:
  1548.                     // This is the second time we've been called.  We've closed the
  1549.                     // file, so just go ahead and send the T_OK_ACK upstream to *finally*
  1550.                     // ACK the T_DISCON_REQ.
  1551.                     
  1552.                     OTAssert("DoDisconnectRequestAck: PBClose failed with error", streamData->fileParamBlock.fileParam.ioResult == noErr);
  1553.                     OTAssert("DoDisconnectRequestAck: File should be closed", streamData->fileRefNum == 0);
  1554.                     streamData->currentState = TS_IDLE;
  1555.                     putnext(q, mp);
  1556.                     break;
  1557.                 
  1558.                 default:
  1559.                     OTDebugBreak("DoDisconnectRequestAck: wackSubstate out of range");
  1560.                     break;
  1561.             }
  1562.             break;
  1563.         
  1564.         case TS_WACK_DREQ9:
  1565.             // If the stream is in TS_WACK_DREQ9, then we've received a T_DISCON_REQ
  1566.             // while we were in the TS_DATA_XFER state, ie we've been asked to disconnect
  1567.             // during the reading process.  Any pending reads have now
  1568.             // completed, so we can just send the T_OK_ACK upstream to ACK the
  1569.             // T_DISCON_REQ.  Well almost )-:  If the file is open we must close
  1570.             // it before doing this.
  1571.  
  1572.             if ( streamData->fileRefNum != 0 ) {
  1573.                 CloseFileAndPostMessage(q, mp);
  1574.                 // ... continue in CustomIOCompletion... TPIFileReadService... DoDisconnectRequestAck
  1575.                 // ie the other branch of this if statement
  1576.             } else {
  1577.                 streamData->currentState = TS_IDLE;
  1578.                 putnext(q, mp);
  1579.             }
  1580.             
  1581.             break;
  1582.             
  1583.         default:
  1584.             OTDebugBreak("DoDisconnectRequestAck: Unexpected state");
  1585.             break;
  1586.     }
  1587.     LOG_EXIT;
  1588. }
  1589.  
  1590. /////////////////////////////////////////////////////////////////////
  1591.  
  1592. static void DoDisconnectIndication(queue_t* q, mblk_t* mp)
  1593.     // This routine is called when a T_DISCON_IND message shows up
  1594.     // on the read-side queue.  This message was put there by
  1595.     // DoDataIndication in response to running into an error reading
  1596.     // the file.  We just switch states and forward the message
  1597.     // upstream.
  1598.     //
  1599.     // Environment: read service routine
  1600. {
  1601.     TRACE_SETUP;
  1602.     PerStreamDataPtr streamData;
  1603.  
  1604.     LOG_ENTRY( "TPIFile:DoDisconnectIndication" );
  1605.  
  1606.     OTAssert("DoDisconnectIndication: Not the read queue", IsReadQ(q) );
  1607.     
  1608.     OTAssert("DoDisconnectIndication: Unexpected message primitive", 
  1609.             ( (GetPrimitive(mp) == T_DISCON_IND) )
  1610.         );
  1611.  
  1612.     streamData = GetPerStreamData(q);
  1613.     OTAssert("DoDisconnectIndication: currentMessage should be nil", streamData->currentMessage == nil);
  1614.     
  1615.     OTAssert("DoDisconnectIndication: Wrong state", streamData->currentState == TS_DATA_XFER);
  1616.     OTAssert("DoDisconnectIndication: File should be closed", streamData->fileRefNum == 0);
  1617.  
  1618.     streamData->currentState = TS_IDLE;
  1619.     putnext(q, mp);
  1620.     LOG_EXIT;
  1621. }
  1622.  
  1623. /////////////////////////////////////////////////////////////////////
  1624. // Read-side service routine
  1625.  
  1626. static int TPIFileReadService(queue_t* q)
  1627.     // This routine is called by STREAMS when someone puts a message
  1628.     // on the read-side queue.  Seeing as we're a driver, the only person
  1629.     // who should be putting messages on our queue.  Some of these messages
  1630.     // have a special meaning, more than what the TPI defines, so we can't
  1631.     // just unilaterally send them upstream.  We have to dispatch them out
  1632.     // to the appropriate handler, which deals with the special cases.
  1633.     //
  1634.     // For example, when the completion routine for a PBReadAsync fires,
  1635.     // it simply queues the T_DATA_IND message on this queue.  However
  1636.     // no one has checked whether the read was successful yet, so we
  1637.     // can't just forward the T_DATA_IND upstream.  Instead this routine
  1638.     // sees the T_DATA_IND and forwards it to DoDataIndication, which 
  1639.     // checks the results of the read and does the right thing.
  1640.     //
  1641.     // Also note that, by handling the data transfer operations through
  1642.     // a this queue, we automatically become a STREAMS flow control
  1643.     // good citizen, just by following the standard STREAMS structure
  1644.     // for a service routine.
  1645.     //
  1646.     // Environment: read service routine
  1647. {
  1648.     TRACE_SETUP;
  1649.     mblk_t *mp;
  1650.     
  1651.     LOG_ENTRY( "TPIFile:TPIFileReadService" );
  1652.  
  1653.     OTAssert("TPIFileReadService: Not the read queue", IsReadQ(q) );
  1654.  
  1655.     // Standard STREAMS flow control structure.  Don't blame me for its bad style (-:
  1656.     
  1657.     while ( (mp = getq(q)) != nil ) {
  1658.         
  1659.         if ((mp->b_datap->db_type < QPCTL) && !canputnext(q)) {
  1660.             putbq(q, mp);
  1661.             goto done;
  1662.         }
  1663.         
  1664.         switch ( GetPrimitive(mp) ) {
  1665.             case T_CONN_CON:
  1666.                 DoConnectConfirm(q, mp);
  1667.                 break;
  1668.             case T_DATA_IND:
  1669.                 DoDataIndication(q, mp);
  1670.                 break;
  1671.             case T_OK_ACK:
  1672.                 DoDisconnectRequestAck(q, mp);
  1673.                 break;
  1674.             case T_DISCON_IND:
  1675.                 DoDisconnectIndication(q, mp);
  1676.                 break;
  1677.             default:
  1678.                 OTDebugBreak("TPIFileReadService: Unexpected type");
  1679.                 break;
  1680.         }
  1681.     
  1682.     }
  1683.  
  1684. done:
  1685.     LOG_EXIT;
  1686.     return (0);
  1687. }
  1688.  
  1689. /////////////////////////////////////////////////////////////////////
  1690. // Static Declaration Structures
  1691.  
  1692. static struct module_info gModuleInfo =  
  1693. {
  1694.     9990,                // Module Number, only useful for debugging
  1695.     "TPIFile",            // Name of module
  1696.     0,                    // Minimum data size
  1697.     INFPSZ,                // Maximum data size
  1698.     65536,                // Hi water mark for queue
  1699.     32768                // Lo water mark for queue
  1700. };
  1701.  
  1702. static struct qinit gReadInit = 
  1703. {
  1704.     TPIFileReadPut,        // Put routine for "incoming" data
  1705.     TPIFileReadService,    // Service routine for "incoming" data
  1706.     TPIFileOpen,        // Our open routine
  1707.     TPIFileClose,         // Our close routine
  1708.     nil,                // No admin routine
  1709.     &gModuleInfo        // Our module_info
  1710. };
  1711.  
  1712. static struct qinit gWriteInit =
  1713. {
  1714.     TPIFileWritePut,    // Put routine for client data
  1715.     nil,                // Service routine for client data
  1716.     nil,                // open  field only used in read-side structure
  1717.     nil,                // close field only used in read-side structure
  1718.     nil,                // admin field only used in read-side structure
  1719.     &gModuleInfo        // Our module_info
  1720. };
  1721.  
  1722. static struct streamtab theStreamTab = 
  1723. {
  1724.     &gReadInit,            // Our read-side qinit structure
  1725.     &gWriteInit,        // Our write-side qinit structure
  1726.     0,                    // We are not a mux, so set this to nil
  1727.     0                    // We are not a mux, so set this to nil
  1728. };
  1729.  
  1730. /////////////////////////////////////////////////////////////////////
  1731. // Macintosh-specific Static Structures
  1732.  
  1733. static struct install_info theInstallInfo =
  1734. {
  1735.     &theStreamTab,            // Stream Tab pointer
  1736.     kOTModIsDriver + kOTModUpperIsTPI,
  1737.                             // Tell OT that we are a driver, not a module
  1738.     SQLVL_MODULE,            // Synchronization level, module level for the moment
  1739.     0,                        // Shared writer list buddy
  1740.     0,                        // Open Transport use - always set to 0
  1741.     0                        // Flag - always set to 0
  1742. };
  1743.  
  1744. // Prototypes for the exported routines below.
  1745.  
  1746. extern Boolean InitStreamModule(TPIFilePortInfoRecordPtr portInfo);
  1747. extern void TerminateStreamModule(void);
  1748. extern install_info* GetOTInstallInfo();
  1749.  
  1750. // Export entry point
  1751.  
  1752. extern Boolean InitStreamModule(TPIFilePortInfoRecordPtr portInfo)
  1753.     // Initialises the module.  Always called at SystemTask time,
  1754.     // so we can call NewIOCompletionProc.
  1755. {    
  1756.     TRACE_SETUP;
  1757.     Boolean result;
  1758.     
  1759.     OTDebugBreak("TPIFile: InitStreamModule");
  1760.     
  1761.     LOG_ENTRY( "TPIFile:InitStreamModule" );
  1762.     OTAssert("InitStreamModule: Bad magic in TPIFilePortInfoRecord",
  1763.                         (portInfo->magic1 == kTPIFilePortInfoMagic1) && 
  1764.                         (portInfo->magic2 == kTPIFilePortInfoMagic2)
  1765.             );
  1766.     #if ! qDebug
  1767.         // portInfo is only used in the above assertion, which is compiled out
  1768.         // if we're not debugging.
  1769.         #pragma unused(portInfo)
  1770.     #endif
  1771.     
  1772.     gCustomIOCompletionUPP = NewIOCompletionProc(CustomIOCompletion);
  1773.     gCleanUpAnyDetachedStreamsTaskID = OTCreateDeferredTask(CleanUpAnyDetachedStreams, nil);
  1774.     
  1775.     result = (gCustomIOCompletionUPP != nil && gCleanUpAnyDetachedStreamsTaskID != 0);
  1776.     
  1777.     LOG_EXIT;
  1778.     return (result);
  1779. }
  1780.  
  1781. extern void TerminateStreamModule(void)
  1782.     // Shuts down the module.  Always called at SystemTask time,
  1783.     // so we can call DisposeRoutineDescriptor.
  1784. {
  1785.     TRACE_SETUP;
  1786.     OSStatus err;
  1787.     
  1788.     LOG_ENTRY( "TPIFile:TerminateStreamModule" );
  1789.     
  1790.     if (gCustomIOCompletionUPP != nil) {
  1791.         DisposeRoutineDescriptor(gCustomIOCompletionUPP);
  1792.         gCustomIOCompletionUPP = nil;
  1793.     }
  1794.     if (gCleanUpAnyDetachedStreamsTaskID != 0) {
  1795.         err = OTDestroyDeferredTask(gCleanUpAnyDetachedStreamsTaskID);
  1796.         OTAssert("TerminateStreamModule: OTDestroyDeferredTask failed", err == noErr);
  1797.         gCleanUpAnyDetachedStreamsTaskID = 0;
  1798.     }
  1799.     
  1800.     // The purpose of gCleanUpAnyDetachedStreamsTaskID is to clean up any
  1801.     // detached streams that are pending the completetion of an asynchronous
  1802.     // _Close.  Unfortunately, in the case where the last stream is closed,
  1803.     // the TerminateStreamModule routine will be executed before the deferred
  1804.     // task runs, so we have just destroyed our last chance of cleaning up
  1805.     // the stream.  We obviously have to avoid this case, so we sit here
  1806.     // waiting for those _Close's to complete.  This is legal because we are
  1807.     // allowed to block inside the TerminateStreamModule routine.
  1808.     //
  1809.     // Well almost...  If the file we're accessing is on AppleShare, this
  1810.     // causes the machine to deadlock.  This is because we're waiting for
  1811.     // PBCloseAsync to complete, but it can't complete until we leave this
  1812.     // routine because AppleShare relies on OT to deliver packets to
  1813.     // complete the request and OT won't be delivering packets until we
  1814.     // leave this routine.  The upshot is that we won't 
  1815.  
  1816.     while ( gModuleList != nil ) {
  1817.         CleanUpAnyDetachedStreams(nil);
  1818.     }
  1819.     
  1820.     // While this assert can never trigger on my module (because of the previous
  1821.     // while loop), it's an excellent idea to have one of these in your code.
  1822.     
  1823.     OTAssert("TerminateStreamModule: Streams are still active", gModuleList == nil);
  1824.  
  1825.     LOG_EXIT;
  1826. }
  1827.  
  1828. extern install_info* GetOTInstallInfo()
  1829.     // Return pointer to install_info to STREAMS.
  1830. {
  1831.     return &theInstallInfo;
  1832. }
  1833.